package com.db4o.odbgen.plugins.java.enhancements;

import java.util.ArrayList;
import java.util.List;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;

import com.db4o.internal.odbgen.TemplatesUtils;
import com.db4o.internal.odbgen.plugins.TemplateList;
import com.db4o.internal.odbgen.plugins.java.enhancements.ChangeDetectorXmlSchemaElement;
import com.db4o.odbgen.OdbgenException;
import com.db4o.odbgen.TargetLanguage;
import com.db4o.odbgen.plugins.MainGeneratorPlugin;
import com.db4o.odbgen.plugins.Plugin;
import com.db4o.odbgen.plugins.java.maingenerator.MainGenerator;
import com.db4o.odbgen.plugins.java.maingenerator.typedxmlschema.TSClass;
import com.db4o.odbgen.plugins.java.maingenerator.typedxmlschema.TSField;
import com.db4o.odbgen.plugins.java.maingenerator.typedxmlschema.TSMethod;

public class ChangeDetector implements Plugin{

	private MainGenerator _mainGenerator;

	/**
	 * Contains the list of standard templates used by {@link ChangeDetector}.
	 * @author liviug
	 */
	private enum Templates implements TemplateList{
		SETTERCODE(Templates.path, "setterCode.vm"),
		SUPPORTCODE(Templates.path, "supportCode.vm");
		private static final String path = "standardPlugins/java/enhancements/changeDetector";

		
		private String _path;
		private String _name;

		Templates(String path, String name){
			this._path = path;
			this._name = name;
		}
		
		/**
		 * Returns the path and the name of the template.
		 */
		public String getQualifiedName() {
			return this._path+"/"+this._name;
		}
	}
	
	/**
	 * See {@link Plugin} for details.
	 */
	@Override
	public List<Class<? extends Plugin>> getPluginDependencies() {
		ArrayList<Class<? extends Plugin>> res = new ArrayList<Class<? extends Plugin>>();
		res.add(MainGenerator.class);
		return res;
	}

	/**
	 * See {@link Plugin} for details.
	 */
	@Override
	public void initialize(MainGeneratorPlugin mainGenerator) {
		this._mainGenerator = (MainGenerator)mainGenerator;
		
	}

	/**
	 * See {@link Plugin} for details.
	 */
	@Override
	public void start() throws OdbgenException {
		for(TSClass tsClass : this._mainGenerator.getTypedXmlSchema().getClasses()){
			//Only classes included inside XmlSchema are considered.
			if(tsClass.getElement()==null) continue;
			if(tsClass.getElement().getBooleanChildValue(ChangeDetectorXmlSchemaElement.CHANGEDETECTOR.getName())){
				this.generateChangeDetectorCode(tsClass);
			}
		}
	}

	/**
	 * Generates the necessary code to support change detection.
	 * @param tsClass The class to generate the code for.
	 * @throws OdbgenException
	 */
	private void generateChangeDetectorCode(TSClass tsClass) throws OdbgenException {

		//Generate needed code inside setter methods.
		Template setterCodeTemplate = TemplatesUtils.getTemplateJar(ChangeDetector.Templates.SETTERCODE.getQualifiedName(), this._mainGenerator.getStandardTemplatesJarName());
		List<TSField> affectedFields = new ArrayList<TSField>();
		for(TSField field : tsClass.getFields()){
			String setterName = this._mainGenerator.getTypedXmlSchema().buildSetterName(field);
			TSMethod setterMethod = tsClass.getMethodByName(setterName);
			if(setterMethod!=null){
				affectedFields.add(field);
				//Add the code to support change detection for this field.
				VelocityContext context = new VelocityContext();
				context.put("field", field);
				String content = TemplatesUtils.merge(setterCodeTemplate, context);
				setterMethod.getContent().insert(0, content);
			}
		}
		
		//Generate support fields and methods inside class.
		Template template = TemplatesUtils.getTemplateJar(ChangeDetector.Templates.SUPPORTCODE.getQualifiedName(), this._mainGenerator.getStandardTemplatesJarName());
		VelocityContext context = new VelocityContext();
		context.put("chFields", affectedFields);
		context.put("typedXmlSchema", this._mainGenerator.getTypedXmlSchema());
		String content = TemplatesUtils.merge(template, context);
		tsClass.getAdditionalContent().append(content);
		
	}


	/**
	 * See {@link Plugin} for details.
	 */
	@Override
	public boolean supportsTargetLanguage(TargetLanguage targetLanguage) {
		if(targetLanguage == TargetLanguage.JAVA){
			return true;
		}
		return false;
	}

}
